#include "config.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>

#include "configure.h"
#include "sysy_lib.h"
#include "timer.h"
#include "net_vip.h"
#include "net_global.h"
#include "net_tool.h"
#include "net_vip_rpc.h"
#include "dbg.h"

/**
 * if enable NETVIP_USE_SUBIF
 * vip set will set to sub interface name like eth0:0
 * else direct add vip to interface like eth0, show with 'ip addr'
 */
#define NETVIP_USE_SUBIF TRUE

int netvip_srv_init();

static worker_handler_t __netvip_monitor_handler;

int netvip_init()
{
        int ret;

        DINFO("netvip init\n");

        ret = netvip_rpc_init();
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

static int __netvip_set(const char *ip)
{
        int ret;
        int stage = 0;
        char if_name[MAX_NAME_LEN], sub_if_name[MAX_NAME_LEN];

        if (nettool_is_use(ip)) {
                ret = EADDRINUSE;
                DERROR("error[%d]:%s.\n", ret, strerror(ret));
                goto err_ret;
        }

        stage = 1;
        ret = nettool_get_if_bynet(if_name, ip);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

#if NETVIP_USE_SUBIF
        stage = 2;
        ret = nettool_get_subif(sub_if_name, if_name);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        stage = 3;
        ret = nettool_set_ip(ip, sub_if_name);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }
#else
        (void) sub_if_name;
        stage = 2;
        ret = nettool_add_ip(ip, if_name);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }
#endif

        DINFO("set vip (%s) to (%s)\n", ip, if_name);
        SINFO(0, "%s, set (%s) to (%s)\n", M_NET_VIP_SET, ip, if_name);

        return 0;
err_ret:
        if (ret != EADDRINUSE) {
                DERROR("stage[%d] -- set vip (%s) failed: err(%d)\n", stage, ip, ret);
                SWARN(0, "%s, set vip (%s) failed: err(%d)\n", M_NET_VIP_SET_ERROR, ip, ret);
        } else {
                ret = 0;
        }

        return ret;
}

static int __netvip_unset(const char *ip)
{
        int ret;
        char if_name[MAX_NAME_LEN];

        if (!nettool_is_use(ip))
                goto out;

        ret = nettool_get_if_byip(if_name, ip);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

#if NETVIP_USE_SUBIF
        ret = nettool_set_down(if_name);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }
#else
        ret = nettool_del_ip(ip, if_name);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }
#endif

        DINFO("unset vip (%s) to (%s)\n", ip, if_name);
        SINFO(0, "%s, unset(%s) to (%s)\n", M_NET_VIP_UNSET, ip, if_name);

out:
        return 0;
err_ret:
        return ret;
}

int netvip_setvip()
{
        int ret, i, fail = 0;
        char ip[MAX_INFO_LEN];

        if (sanconf.iscsi_vip.vip_count == 0) {
                DWARN("the VIP function is disabled, please check lich.conf.\n");
                goto out;
        }

        for (i = 0; i < sanconf.iscsi_vip.vip_count; i++) {
                strcpy(ip, sanconf.iscsi_vip.vip[i]);
                if (!nettool_is_ip(ip)) {
                        DERROR("please check lich.conf, (%s) is not a valid ip address\n", ip);
                        ret = EFAULT;
                        GOTO(err_ret, ret);
                }

                ret = __netvip_set(ip);
                if (unlikely(ret)) {
                        DERROR("vip:%s set fail, ret:%d\n", ip, ret);
                        fail = ret;
                        continue;
                }
        }

        if (unlikely(fail)) {
                GOTO(err_ret, fail);
        }

out:
        return 0;
err_ret:
        return ret;

}

int netvip_check()
{
        int ret, i, is_local = 0, fail = 0;
        char ip[MAX_INFO_LEN];
        nid_t master;

        if (sanconf.iscsi_vip.vip_count == 0)
                goto out;

        master = *net_getadmin();
        is_local = net_islocal(&master);

        for (i = 0; i < sanconf.iscsi_vip.vip_count; i++) {
                strcpy(ip, sanconf.iscsi_vip.vip[i]);
                if (!nettool_is_ip(ip)) {
                        DERROR("please check lich.conf, (%s) is not a valid ip address\n", ip);
                        ret = EFAULT;
                        GOTO(err_ret, ret);
                }

                if (is_local) {
                        ret = __netvip_set(ip);
                        if (unlikely(ret)) {
                                DERROR("vip:%s set fail, ret:%d\n", ip, ret);
                                fail = ret;
                                continue;
                        }
                } else {
                        ret = __netvip_unset(ip);
                        if (unlikely(ret)) {
                                DERROR("vip:%s unset fail, ret:%d\n", ip, ret);
                                fail = ret;
                                continue;
                        }
                }
        }

        if (unlikely(fail)) {
                GOTO(err_ret, fail);
        }
out:
        return 0;
err_ret:
        return ret;
}

int netvip_destroy()
{
        int ret, i;
        char ip[MAX_INFO_LEN];

        for (i = 0; i < sanconf.iscsi_vip.vip_count; i++) {
                strcpy(ip, sanconf.iscsi_vip.vip[i]);

                if (!nettool_is_ip(ip) || !nettool_is_use(ip))
                        goto out;

                ret = __netvip_unset(ip);
                if (unlikely(ret)) {
                        GOTO(err_ret, ret);
                }
        }

out:
        return 0;
err_ret:
        return ret;
}

int iser_netvip_is_vip(struct sockaddr_in *addr)
{
        int i;
        char ip[MAX_INFO_LEN];

        for (i = 0; i < sanconf.iscsi_vip.vip_count; i++) {
                strcpy(ip, sanconf.iscsi_vip.vip[i]);

                if (!nettool_is_ip(ip)) {
                        DERROR("please check lich.conf, (%s) is not a valid ip address\n", ip);
                } else {
                        if(nettool_is_equality(ip, addr))
                                return 1;
                }
        }

        return 0;
}

int netvip_is_vip(int conn_fd)
{
        int ret, i = 0;
        char ip[MAX_INFO_LEN];
        struct sockaddr_in addr;
        socklen_t slen = sizeof(struct sockaddr_storage);

        if (sanconf.iscsi_vip.vip_count == 0)
                return 0;

        ret = getsockname(conn_fd, (struct sockaddr *)&addr, &slen);
        if (unlikely(ret)) {
                return 0;
        }

        for (i = 0; i < sanconf.iscsi_vip.vip_count; i++) {
                strcpy(ip, sanconf.iscsi_vip.vip[i]);

                if (!nettool_is_ip(ip)) {
                        DERROR("please check lich.conf, (%s) is not a valid ip address\n", ip);
                } else {
                        if(nettool_is_equality(ip, &addr))
                                return 1;
                }
        }

        return 0;
}

int iser_netvip_in_vipnet(struct sockaddr_in *addr)
{
        int i;
        char ip[MAX_INFO_LEN];

        for (i = 0; i < sanconf.iscsi_vip.vip_count; i++) {
                strcpy(ip, sanconf.iscsi_vip.vip[i]);

                if (!nettool_is_ip(ip)) {
                        DERROR("please check lich.conf, (%s) is not a valid ip address\n", ip);
                } else {
                        if(nettool_in_ipnet(ip, addr))
                                return 1;
                }
        }

        return 0;
}

int netvip_in_vipnet(int conn_fd)
{
        int ret, i;
        char ip[MAX_INFO_LEN];
        struct sockaddr_in addr;
        socklen_t slen = sizeof(struct sockaddr_storage);

        if (sanconf.iscsi_vip.vip_count == 0)
                return 0;

        ret = getsockname(conn_fd, (struct sockaddr *)&addr, &slen);
        if (unlikely(ret)) {
                return 0;
        }

        for (i = 0; i < sanconf.iscsi_vip.vip_count; i++) {
                strcpy(ip, sanconf.iscsi_vip.vip[i]);

                if (!nettool_is_ip(ip)) {
                        DERROR("please check lich.conf, (%s) is not a valid ip address\n", ip);
                } else {
                        if(nettool_in_ipnet(ip, &addr))
                                return 1;
                }
        }

        return 0;
}

int netvip_check_vip(const char *vip)
{
        int ret, found = 0, i;
        char ip[MAX_INFO_LEN];
        nid_t master;

        if (sanconf.iscsi_vip.vip_count == 0) {
                ret = EFAULT;
                GOTO(err_ret, ret);
        }

        for (i = 0; i < sanconf.iscsi_vip.vip_count; i++) {
                strcpy(ip, sanconf.iscsi_vip.vip[i]);
                if (!nettool_is_ip(ip)) {
                        DERROR("please check lich.conf, (%s) is not a valid ip address\n", ip);
                        ret = EFAULT;
                        GOTO(err_ret, ret);
                }

                if (strncmp(vip, ip, strlen(vip)) == 0) {
                        found = 1;
                        break;
                }

        }

        if (found) {
                master = *net_getadmin();
                if (!net_islocal(&master)) {
                        netvip_destroy();
                        // just try
                        netvip_rpc_master_setvip(&master, vip, strlen(vip) + 1);

                        ret = ENETRESET;
                        GOTO(err_ret, ret);
                }

                ret = __netvip_set(vip);
                if (unlikely(ret))
                        GOTO(err_ret, ret);
        } else {
                DWARN("vip (%s) is not a conf iscsi vip\n", vip);
                ret = ENXIO;
                GOTO(err_ret, ret);
        }

        return 0;
err_ret:
        return ret;
}

int netvip_getip_byid(const nid_t *nid, uint32_t vip, char *value, int *valuelen)
{
        if (net_islocal(nid)) {
                return nettool_get_ip_byvip(vip, value, valuelen);
        } else {
                return netvip_rpc_getip_byvip(nid, vip, value, valuelen);
        }

        return 0;
}

static int __netvip_monitor_worker()
{
        int ret;

        ret = netvip_check();
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = timer1_settime(&__netvip_monitor_handler, USEC_PER_SEC);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        DERROR("netvip monitor err (%d) %s\n", ret, strerror(ret));

        ret = timer1_settime(&__netvip_monitor_handler, USEC_PER_SEC);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        //always retrun true here
        return 0;
}

int netvip_srv_init()
{
        int ret;

        ret = timer1_create(&__netvip_monitor_handler, "netvip_monitor", __netvip_monitor_worker, NULL);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = timer1_settime(&__netvip_monitor_handler, USEC_PER_SEC);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        return 0;
err_ret:
        return ret;
}
